﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Volo.Abp.Identity;
using Volo.Abp.SettingManagement;
using Volo.Abp.Settings;
using Volo.Abp.Threading;
using Volo.Abp.Uow;
using Microsoft.Extensions.Localization;
using Volo.Abp.Identity.Settings;
using Volo.Abp.Authorization.Permissions;
using System.Data;
using Volo.Abp.PermissionManagement;
using SanJ.FreeCare.Localization;
using SanJ.FreeCare.Abp.Authorization.Users;
using SanJ.FreeCare.Abp.Authorization.Roles;
using SanJ.FreeCare.Abp.Security;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Caching;
using Volo.Abp.Security.Claims;

namespace SanJ.FreeCare.Authorization.Users
{
    /// <summary>
    /// User manager.
    /// Used to implement domain logic for users.
    /// Extends <see cref="IdentityUserManager"/>.
    /// </summary>
    public class UserManager : IdentityUserManager
    {
        private readonly IUnitOfWorkManager _unitOfWorkManager;
        private readonly IStringLocalizer _stringLocalizer;
        private readonly ISettingProvider _settingProvider;
        private readonly IPasswordHasher<IdentityUser> _passwordHasher;
        private readonly IPermissionManager _permissionManager;

        public UserManager(IdentityUserStore store,
            IIdentityRoleRepository roleRepository,
            IIdentityUserRepository userRepository,
            IOptions<IdentityOptions> optionsAccessor,
            IPasswordHasher<IdentityUser> passwordHasher,
            IEnumerable<IUserValidator<IdentityUser>> userValidators,
            IEnumerable<IPasswordValidator<IdentityUser>> passwordValidators,
            ILookupNormalizer keyNormalizer,
            IdentityErrorDescriber errors,
            IServiceProvider services,
            ILogger<IdentityUserManager> logger,
            ICancellationTokenProvider cancellationTokenProvider,
            IOrganizationUnitRepository organizationUnitRepository,
            ISettingProvider settingProvider,
            IStringLocalizer<FreeCareResource> localizer,
            IUnitOfWorkManager unitOfWorkManager,
            IPermissionManager permissionManager,
            IDistributedEventBus distributedEventBus,      //   7.0和8.0 有區別   
            IIdentityLinkUserRepository identityLinkUserRepository, 
            IDistributedCache<AbpDynamicClaimCacheItem> dynamicClaimCache

            ) :
            base(store,
                roleRepository,
                userRepository,
                optionsAccessor,
                passwordHasher,
                userValidators,
                passwordValidators,
                keyNormalizer,
                errors,
                services,
                logger,
                cancellationTokenProvider,
                organizationUnitRepository,
                settingProvider,
                distributedEventBus,
                identityLinkUserRepository,
                dynamicClaimCache)
        {
            _passwordHasher = passwordHasher;
            _unitOfWorkManager = unitOfWorkManager;
            _settingProvider = settingProvider;
            _stringLocalizer = localizer;
            _permissionManager = permissionManager;
        }

        public virtual async Task<IdentityUser> GetUserOrNullAsync(UserIdentifier userIdentifier)
        {
            using (var uow = _unitOfWorkManager.Begin(
                      requiresNew: true, isTransactional: false))
            {
                return await FindByIdAsync(userIdentifier.UserId.ToString());
            }
        }

        public IdentityUser GetUserOrNull(UserIdentifier userIdentifier)
        {
            return AsyncHelper.RunSync(() => GetUserOrNullAsync(userIdentifier));
        }

        public async Task<IdentityUser> GetUserAsync(UserIdentifier userIdentifier)
        {
            var user = await GetUserOrNullAsync(userIdentifier);
            if (user == null)
            {
                throw new Exception("There is no user: " + userIdentifier);
            }

            return user;
        }

        public IdentityUser GetUser(UserIdentifier userIdentifier)
        {
            return AsyncHelper.RunSync(() => GetUserAsync(userIdentifier));
        }

        public override Task<IdentityResult> SetRolesAsync(IdentityUser user, IEnumerable<string> roleNames)
        {
            if (user.UserName != "admin")
            {
                return base.SetRolesAsync(user, roleNames);
            }

            // Always keep admin role for admin user
            var roles = roleNames.ToList();
            roles.Add(StaticRoleNames.Host.Admin);
            roleNames = roles.ToArray();

            return base.SetRolesAsync(user, roleNames);
        }

        public Task SetGrantedPermissionsAsync(IdentityUser user, IEnumerable<PermissionDefinition> permissions)
        {
            CheckPermissionsToUpdate(user, permissions);

            //await base.SetGrantedPermissionsAsync(user, permissions);
            foreach (var item in permissions)
            {
                _permissionManager.SetForUserAsync(user.Id, item.Name, true);
            }
            return Task.CompletedTask;
        }

        public async Task<string> CreateRandomPassword()
        {
            var passwordComplexitySetting = new PasswordComplexitySetting
            {
                RequireDigit = await _settingProvider.GetAsync<bool>(
                    IdentitySettingNames.Password.RequireDigit
                ),
                RequireLowercase = await _settingProvider.GetAsync<bool>(
                    IdentitySettingNames.Password.RequireLowercase
                ),
                RequireNonAlphanumeric = await _settingProvider.GetAsync<bool>(
                    IdentitySettingNames.Password.RequireNonAlphanumeric
                ),
                RequireUppercase = await _settingProvider.GetAsync<bool>(
                    IdentitySettingNames.Password.RequireUppercase
                ),
                RequiredLength = await _settingProvider.GetAsync<int>(
                    IdentitySettingNames.Password.RequiredLength
                )
            };

            var upperCaseLetters = "ABCDEFGHJKLMNOPQRSTUVWXYZ";
            var lowerCaseLetters = "abcdefghijkmnopqrstuvwxyz";
            var digits = "0123456789";
            var nonAlphanumerics = "!@$?_-";

            string[] randomChars =
            {
                upperCaseLetters,
                lowerCaseLetters,
                digits,
                nonAlphanumerics
            };

            var rand = new Random(Environment.TickCount);
            var chars = new List<char>();

            if (passwordComplexitySetting.RequireUppercase)
            {
                chars.Insert(rand.Next(0, chars.Count),
                    upperCaseLetters[rand.Next(0, upperCaseLetters.Length)]
                );
            }

            if (passwordComplexitySetting.RequireLowercase)
            {
                chars.Insert(rand.Next(0, chars.Count),
                    lowerCaseLetters[rand.Next(0, lowerCaseLetters.Length)]
                );
            }

            if (passwordComplexitySetting.RequireDigit)
            {
                chars.Insert(rand.Next(0, chars.Count),
                    digits[rand.Next(0, digits.Length)]
                );
            }

            if (passwordComplexitySetting.RequireNonAlphanumeric)
            {
                chars.Insert(rand.Next(0, chars.Count),
                    nonAlphanumerics[rand.Next(0, nonAlphanumerics.Length)]
                );
            }

            for (var i = chars.Count; i < passwordComplexitySetting.RequiredLength; i++)
            {
                var rcs = randomChars[rand.Next(0, randomChars.Length)];
                chars.Insert(rand.Next(0, chars.Count),
                    rcs[rand.Next(0, rcs.Length)]
                );
            }

            return new string(chars.ToArray());
        }

        private void CheckPermissionsToUpdate(IdentityUser user, IEnumerable<PermissionDefinition> permissions)
        {
            //if (user.Name == "admin" &&
            //    (!permissions.Any(p => p.Name == HopePermissions.Pages_Administration_Roles_Edit) ||
            //     !permissions.Any(p => p.Name == HopePermissions.Pages_Administration_Users_ChangePermissions)))
            //{
            //    throw new UserFriendlyException(L("YouCannotRemoveUserRolePermissionsFromAdminUser"));
            //}
        }

        private new string L(string name)
        {
            return _stringLocalizer.GetString(FreeCareConsts.LocalizationSourceName, name);
        }

        protected string L(string name, params object[] args) => string.Format(L(name), args);

        public override async Task<IdentityResult> ChangePasswordAsync(IdentityUser user, string currentPassword,
            string newPassword)
        {
            //await CheckRecentPasswordsIfNeeded(user, currentPassword, newPassword);

            var result = await base.ChangePasswordAsync(user, currentPassword, newPassword);

            //await StoreRecentPasswordIfNeeded(user, result);

            return result;
        }

        private Task CheckRecentPasswordsIfNeeded(IdentityUser user, string newPassword)
        {
            throw new NotImplementedException();
            //return CheckRecentPasswordsIfNeededInternal(user, user.PasswordHash, newPassword);
        }

        private Task CheckRecentPasswordsIfNeeded(IdentityUser user, string currentPassword, string newPassword)
        {
            throw new NotImplementedException();
            //var currentPasswordHash = _passwordHasher.HashPassword(user, currentPassword);

            //return CheckRecentPasswordsIfNeededInternal(user, currentPasswordHash, newPassword);
        }

        //private async Task CheckRecentPasswordsIfNeededInternal(
        //    User user,
        //    string currentPasswordHash,
        //    string newPassword)
        //{
        //    var isCheckingLastXPasswordEnabled = await _settingManager.GetSettingValueAsync<bool>(
        //        AppSettings.UserManagement.Password.EnableCheckingLastXPasswordWhenPasswordChange
        //    );

        //    if (!isCheckingLastXPasswordEnabled)
        //    {
        //        return;
        //    }

        //    var newPasswordAndCurrentPasswordVerificationResult = _passwordHasher.VerifyHashedPassword(
        //        user,
        //        currentPasswordHash,
        //        newPassword
        //    );

        //    var checkingLastXPasswordCount = await _settingManager.GetSettingValueAsync<int>(
        //        AppSettings.UserManagement.Password
        //            .CheckingLastXPasswordCount
        //    );

        //    if (newPasswordAndCurrentPasswordVerificationResult != PasswordVerificationResult.Failed)
        //    {
        //        throw new UserFriendlyException(
        //            L("NewPasswordMustBeDifferentThenLastXPassword", checkingLastXPasswordCount)
        //        );
        //    }

        //    var recentPasswords = await _recentPasswords.GetAll()
        //        .Where(rp => rp.UserId == user.Id)
        //        .OrderByDescending(rp => rp.CreationTime)
        //        .Take(checkingLastXPasswordCount)
        //        .ToListAsync();

        //    foreach (var recentPassword in recentPasswords)
        //    {
        //        var recentPasswordVerificationResult = _passwordHasher.VerifyHashedPassword(
        //            user,
        //            recentPassword.Password,
        //            newPassword
        //        );

        //        if (recentPasswordVerificationResult != PasswordVerificationResult.Failed)
        //        {
        //            throw new UserFriendlyException(
        //                L("NewPasswordMustBeDifferentThenLastXPassword", checkingLastXPasswordCount)
        //            );
        //        }
        //    }
        //}

    }
}
